CAS_ARCHITECTURE.md

Single source of truth. Every session reads this first. Do not infer. Do not guess. Read and execute.


SYSTEM OVERVIEW

One Cloudflare Worker (trainingbiz-admin-clerk) serves all domains and subdomains. It is a lookup-and-render engine. It contains zero business data. Everything that changes lives in KV or R2 and is resolved at runtime from the URL context.

Three templates in R2 — CSS/structure only, no hardcoded content:

One KV namespace: CAC_KV (id: 0ed03de63f354acaa853500c3ee3473c) One D1 database: claritysystems-db (id: 56ec1996-e7a2-4a81-a013-009d9256632b) One R2 bucket: cac-assets One browser binding: BROWSER (Puppeteer for PDF generation)


URL ROUTING — HOW THE WORKER KNOWS EVERYTHING

Host header:     newrise.claritysystems.work   → domain:{hostname} → tp_key
Path segment 1:  /sales-technical              → course_id
Path segment 2:  /20-05-26-penang-stgiles      → event_slug
Path segment 3:  /enquire                      → route type

Three route types:

GET  /{event_slug}          → render landing page
GET  /{event_slug}/enquire  → render enquiry form (event vars as read-only block)
POST /{event_slug}/enquire  → process submission → generate docs → send email + notify

Full lookup chain on every POST:

Host                     → domain:{hostname}                  → tp_key, payment_paths, notify targets
course_id (from path)    → course:{course_id}                 → title, pricing, email copy, includes, logistics
event slug + course_id   → event:{course_id}:{event_slug}     → date, venue, trainer_key, tp_key
tp_key (from event)      → tp:{tp_key}                        → TP identity + bank
trainer_key (from event) → trainer:{trainer_key}              → bio, photo_r2_key, credentials
                           owner:cac                          → CAC identity + bank (always fetched)
                           invoice:last_number                → next invoice number (always fetched)

Event slug format: DD-MM-YY-city-venue Example: event:sales-technical:20-05-26-penang-stgiles

New event process: Create KV key event:{course_id}:{slug} in Cloudflare dashboard. The slug IS the URL. No code change. New course process: Create KV key course:{course_id} + schedule:{course_id}. No code change. New TP process: Create KV key tp:{tp_key} + domain:{subdomain} + one DNS record. No code change.


KV SCHEMA — COMPLETE

domain:{hostname}

{
  "tp_key": "newrise",
  "payment_paths": ["grant"],
  "notify_telegram_chat_id": "7918071148",
  "notify_email": "[email protected]"
}

Keys to create:


owner:cac

{
  "name": "Clarity Awareness Cognitive Architecture Systems",
  "legal_name": "Clarity Awareness Coaching",
  "contact_person": "Mak Sau Kuen (Lovinia)",
  "address": "8-6-14 Persiaran Paya Terubong 1, 11900, Bayan Lepas, Pulau Pinang, Malaysia",
  "email": "[email protected]",
  "whatsapp": "+60184012108",
  "website": "claritysystems.work",
  "logo_r2_key": "logo-owner.png",
  "logo_url": "https://d19d19e3380738cfbf190c51e726202e.r2.cloudflarestorage.com/cac-assets",
  "bank_account_name": "Clarity Awareness Coaching",
  "bank_name": "Bank Rakyat",
  "bank_account_number": "1102263454",
  "footer_name": "Clarity Awareness Cognitive Architecture Systems · claritysystems.work"
}

tp:newrise

{
  "name": "Newrise Learning Sdn Bhd",
  "address": "2-3-11, Bangunan Lip Sin, Lebuh Pekaka 1, Sungai Dua, 11700 Gelugor, Penang, Malaysia",
  "contact_email": "[email protected]",
  "contact_email_2": "[email protected]",
  "contact_phone": "018-961 1336",
  "bank_account_name": "Newrise Learning Sdn Bhd",
  "bank_name": "Malayan Banking Berhad (Maybank)",
  "bank_account_number": "5070 9567 8082",
  "sst_number": "P11-1902-32000013",
  "logo_r2_key": "logo-newrise.png",
  "logo_url": "https://pub-1910845bdea34f268e2eacab46e246ed.r2.dev/logo-newrise.png",
  "notify_telegram_chat_id": "7918071148"
}

course:sales-technical

Pricing logic (worker enforces this — not stored in KV):

form_type hrdc_option Price basis Proforma
corporate direct base_price minus direct_discount_pct Yes
corporate grant base_price plus grant_surcharge_pct Yes
public grant rate_per_seat × pax Yes
public direct Stripe link only No

Provider path permanently removed. Do not add it back.

{
  "title": "Sales for Technical People & Non-Sales Professionals",
  "hrdc_programme_number": "####",
  "duration_label": "2-Day Corporate Training Programme",
  "duration_days": 2,
  "corporate": {
    "base_price": 22000,
    "direct_discount_pct": 10,
    "grant_surcharge_pct": 4
  },
  "public": {
    "rate_per_seat": 3240,
    "stripe_link": ""
  },
  "pax_min": 7,
  "pax_max": 40,
  "currency": "MYR",
  "includes_note": "Proprietary Buyer Profiling Assessment · 4E Framework Sales Conversation Blueprint · Digital Assets including Diagnostic Assessments · Activity Workbook · Online Continuous Training (where applicable)",
  "logistics_note": "Physical materials, venue, catering, trainer meals and accommodation to be supplied by client organiser. Physical materials available upon request, billed separately.",
  "programme_desc": "A 2-day structured training programme designed to equip technical and non-sales professionals with a repeatable, authentic selling framework. Participants leave with a personalised sales conversation blueprint, calibrated buyer profiling capability, and a clear system for closing without burnout.",
  "email_copy": {
    "corporate_grant": {
      "subject": "Your Training Proposal via HRDC Grant — ",
      "body_intro": "Thank you for your interest in <strong>Sales for Technical &amp; Non-Sales Professionals</strong>.",
      "body_next_step": "You have selected the HRDC Grant path. A 4% HRDC surcharge applies. Your invoice and payment details reflect those of the appointed Training Provider ().<br><br><strong>Attached:</strong> Tentative Course Schedule, Trainer Profile, and Proforma Invoice ().<br><br><strong>Next steps:</strong> Reply to this email with your selected venue and confirmed dates. If you have any questions, include them and your appointed Training Provider will give you a call to confirm venue, date and payment procedures."
    },
    "corporate_direct": {
      "subject": "Your Training Proposal (10% Direct Discount) — ",
      "body_intro": "Thank you for your interest in <strong>Sales for Technical &amp; Non-Sales Professionals</strong>.",
      "body_next_step": "Your 10% direct payment discount is reflected in the attached Proforma Invoice ().<br><br><strong>Attached:</strong> Tentative Course Schedule, Trainer Profile, and Proforma Invoice.<br><br><strong>Next steps:</strong> To confirm your booking, reply to this email. A payment link will be provided upon confirmation."
    },
    "public_grant": {
      "subject": "Your Public Training Seat via HRDC Grant — ",
      "body_intro": "Thank you for your interest in <strong>Sales for Technical &amp; Non-Sales Professionals</strong>.",
      "body_next_step": "You have selected the HRDC Grant path. Your invoice and payment details reflect those of the appointed Training Provider ().<br><br><strong>Attached:</strong> Tentative Course Schedule, Trainer Profile, and Proforma Invoice ().<br><br><strong>Next steps:</strong> Reply to this email to confirm your seat. Your Training Provider will contact you to confirm payment procedures."
    }
  },
  "terms": {
    "corporate_grant": "This proforma is valid for <strong>60 days</strong> from the date of issue.<br><br>Rescheduling: Permitted with minimum 14 days notice at no additional charge.<br><br>Grant application and submission remain the responsibility of the employer. A 4% HRDC surcharge applies and is reflected in this invoice.",
    "corporate_direct": "This proforma is valid for <strong>60 days</strong> from the date of issue.<br><br>Preferred payment: Direct bank transfer or Stripe checkout link (provided upon confirmation).<br><br>Rescheduling: Permitted with minimum 14 days notice at no additional charge.<br><br>Cancellation: Deposit is non-refundable within 14 days of training date.",
    "public_grant": "This proforma is valid for <strong>60 days</strong> from the date of issue.<br><br>Rescheduling: Permitted with minimum 14 days notice at no additional charge.<br><br>Grant application and submission remain the responsibility of the employer. A 4% HRDC surcharge applies and is reflected in this invoice."
  },
  "payment_terms": {
    "corporate_grant": "50% deposit upon confirmation. Remaining 50% no later than 7 days before training date.",
    "corporate_direct": "50% deposit upon confirmation. Remaining 50% no later than 7 days before training date.<br><br><strong>Direct Payment Bonuses</strong><br>10% discount reflected in this invoice. Plus: 70% off tickets to VIP-selected future public events (limited seats per organisation). Bonus seats will be confirmed upon full payment.",
    "public_grant": "Full payment upon confirmation. Payment must be received no later than 7 days before training date."
  },
  "hrdc_block": "<span class=\"hrdc-label\">HRDC Claimable — SBL-Khas Scheme</span><p>This programme is available under the HRDC SBL-Khas grant scheme. Employers registered with HRD Corp may submit a claim prior to the training date. Grant application and submission remain the responsibility of the employer.</p>"
}

schedule:sales-technical

{
  "day1_title": "Foundation & Framework",
  "day2_title": "Application & Closing",
  "day1": [
    { "time": "9:00 – 9:15",   "title": "Introduction and Orientation",                                          "detail": "",                                                               "break": false },
    { "time": "9:15 – 10:30",  "title": "Everybody Sells",                                                       "detail": "The Technical & Professional Moat in the Age of AI",              "break": false },
    { "time": "10:30 – 10:45", "title": "Morning Break",                                                         "detail": "",                                                               "break": true  },
    { "time": "10:45 – 13:00", "title": "The 5 Hidden Blocks to Selling Naturally and Authentically",            "detail": "",                                                               "break": false },
    { "time": "13:00 – 14:00", "title": "Lunch Break",                                                           "detail": "",                                                               "break": true  },
    { "time": "14:00 – 15:30", "title": "The 4E Framework",                                                      "detail": "Diagnosing Buying Profiles in Under 2 Minutes",                   "break": false },
    { "time": "15:30 – 15:45", "title": "Afternoon Break",                                                       "detail": "",                                                               "break": true  },
    { "time": "15:45 – 16:45", "title": "Applying the 4E Framework",                                             "detail": "Individual Case Application",                                    "break": false },
    { "time": "16:45 – 17:00", "title": "Discussions and Questions",                                             "detail": "",                                                               "break": false }
  ],
  "day2": [
    { "time": "9:00 – 9:15",   "title": "Recap of the 4E Framework & Application in Individual Use Case",        "detail": "",                                                               "break": false },
    { "time": "9:15 – 10:30",  "title": "Authentic Selling Blueprint",                                           "detail": "Creating Your High-Touch Sales Boilerplate",                     "break": false },
    { "time": "10:30 – 10:45", "title": "Morning Break",                                                         "detail": "",                                                               "break": true  },
    { "time": "10:45 – 13:00", "title": "Coaching & Fine-Tuning",                                                "detail": "Your Sales Conversation and Overcoming Places You Get Stuck",    "break": false },
    { "time": "13:00 – 14:00", "title": "Lunch Break",                                                           "detail": "",                                                               "break": true  },
    { "time": "14:00 – 15:30", "title": "The Inner & Outer Game of Closing",                                     "detail": "Building Long-Term Stamina in Sales Without Burnout",            "break": false },
    { "time": "15:30 – 15:45", "title": "Afternoon Break",                                                       "detail": "",                                                               "break": true  },
    { "time": "15:45 – 16:30", "title": "Coaching and Discussion",                                               "detail": "",                                                               "break": false },
    { "time": "16:45 – 17:00", "title": "Sharing and Presentation of Learning Success",                          "detail": "",                                                               "break": false }
  ]
}

event:sales-technical:20-05-26-penang-stgiles

{
  "date_day1": "Tuesday, 20 May 2026",
  "date_day2": "Wednesday, 21 May 2026",
  "venue": "St Giles Wembley, 183 Jalan Magazine, George Town, Penang",
  "city": "Penang",
  "organizer_name": "Newrise Learning Sdn Bhd",
  "organizer_contact": "018-961 1336",
  "trainer_key": "761124145532",
  "tp_key": "newrise",
  "course_id": "sales-technical"
}

trainer:{NRIC}

Key pattern: trainer: + Malaysian NRIC or passport number. Never use name. Example key: trainer:761124145532

{
  "name": "Lovinia Xavier",
  "title": "Principal Trainer & Consultant",
  "bio": "[Lovinia's bio — to be written]",
  "credentials": [
    "[Credential 1]",
    "[Credential 2]"
  ],
  "photo_r2_key": "photo-lovinia.jpg",
  "signature_r2_key": ""
}

invoice:last_number

131155

(Already exists in KV. Verify current value before proceeding.)


TEMPLATE VARIABLES — COMPLETE LIST

template-proforma.html injects:

template-course-schedule.html injects:

template-trainer-profile.html injects: (template not yet built)

``


D1 SCHEMA — claritysystems-db

Table: enquiries

CREATE TABLE enquiries (
  id               TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
  invoice_number   TEXT NOT NULL,
  submitted_at     TEXT NOT NULL DEFAULT (datetime('now')),
  tp_key           TEXT,
  course_id        TEXT NOT NULL,
  event_slug       TEXT NOT NULL,
  form_type        TEXT NOT NULL CHECK(form_type IN ('corporate','public')),
  hrdc_option      TEXT NOT NULL CHECK(hrdc_option IN ('direct','grant')),
  company_name     TEXT,
  contact_person   TEXT NOT NULL,
  email            TEXT NOT NULL,
  phone            TEXT,
  pax              INTEGER,
  company_address  TEXT,
  hrdc_levy_number TEXT,
  status           TEXT NOT NULL DEFAULT 'new'
);

Table: email_events

CREATE TABLE email_events (
  id              TEXT PRIMARY KEY,
  enquiry_id      TEXT NOT NULL REFERENCES enquiries(id),
  resend_email_id TEXT,
  event_type      TEXT NOT NULL,
  metadata        TEXT,
  created_at      TEXT NOT NULL DEFAULT (datetime('now'))
);

WORKER SECRETS (set in Cloudflare dashboard → Worker settings → Variables & Secrets)

Secret name Value
RESEND_API_KEY [existing — do not rotate]
TELEGRAM_BOT_TOKEN 8603272908:AAH5R-iw_OGEfTWxJMNKOYWTV_qOc9RSfXo
TELEGRAM_CHAT_ID 7918071148

WORKER BINDINGS — wrangler.toml

name = "trainingbiz-admin-clerk"
main = "src/index.js"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]

[[d1_databases]]
binding = "DB"
database_name = "claritysystems-db"
database_id = "56ec1996-e7a2-4a81-a013-009d9256632b"

[[kv_namespaces]]
binding = "CAC_KV"
id = "0ed03de63f354acaa853500c3ee3473c"

[[r2_buckets]]
binding = "ASSETS"
bucket_name = "cac-assets"

[browser]
binding = "BROWSER"

R2 BUCKET CONTENTS — cac-assets

File Status
template-proforma.html Exists — hardcoded strings removed
template-course-schedule.html Exists — hardcoded content removed, convert to slot renderer)
CAC Logo 2026_b.png Exists
trainer_profile.pdf Exists — rename to trainer_761124145532.pdf
hrdc_registration_guide.pdf - human has no idea what this is.
logo-newrise.png in R2.

BUILD SEQUENCE

Phase 0 — THIS SESSION (complete)

CAS_ARCHITECTURE.md written. SESSION_LOG.md written.

Phase 1 — Human actions in Cloudflare dashboard (no code) (completed)

Do not proceed to Phase 2 until all Phase 1 actions are confirmed done and logged.

  1. Rename worker cac-corporate-enquirytrainingbiz-admin-clerk
  2. Delete workers: hrdc-grant-public-training, newrise-enquiry
  3. Delete D1: newrise-db (already deleted per session)
  4. Delete R2: newrise-assets (verify empty first)
  5. Delete KV namespace: NR_KV (already deleted per session)
  6. In CAC_KV: create/update all keys listed in KV SCHEMA section above
  7. In cac-assets R2: rename trainer_profile.pdftrainer_761124145532.pdf
  8. Confirm/upload: photo-lovinia.jpg, logo-newrise.png
  9. Verify invoice:last_number current value

Phase 2 — Templates (one session, one file at a time) (completed)

Session reads: CAS_ARCHITECTURE.md → Template Variables section Builds:

  1. Strip template-proforma.html to CSS-only shell with ``
  2. Strip template-course-schedule.html to CSS-only shell with slot renderer
  3. Build template-trainer-profile.html from scratch Each file: build → confirm → upload to R2 → log in SESSION_LOG.md → proceed

Phase 3 — D1 migration

Session reads: CAS_ARCHITECTURE.md → D1 SCHEMA section Builds: One .sql migration file. Run via Cloudflare dashboard or wrangler. Confirm existing enquiries table schema matches or migrate.

Phase 4 — Worker rewrite

Session reads: CAS_ARCHITECTURE.md (full) + existing src/index.js Builds: Complete rewrite of trainingbiz-admin-clerk/src/index.js

Phase 5 — Eleventy integration

Session reads: CAS_ARCHITECTURE.md → URL Routing + KV Schema sections Builds: Eleventy config reads event:{course_id}:{slug} from KV at build time Depends on: Phase 1 complete (KV entries exist)


OPERATIONAL RULES (every session must enforce)

  1. Read CAS_ARCHITECTURE.md first. Every session. No exceptions.
  2. Read SESSION_LOG.md second. Know exactly where to resume.
  3. Lock before build. Confirm the unit scope with user before writing any code.
  4. Log after each completed unit. Not at session end. After each discrete deliverable.
  5. One unit per session. If rate limit approaches, commit what is done, log it, stop cleanly.
  6. Never infer missing values. If a field is blank in this document, surface it to the user before proceeding.
  7. KV schema is the contract. Any deviation must be approved and updated here before code reflects it.

Owner notes :

  1. HRDC training programme number for current course still pending. KV value is still blank. - question : what should the key and value be? Every course has a different training programme number.
  2. All trainer profiles go by trainer_NRIC# i.e "trainer" + underscore + Malaysian NRIC or passport number